package com.etheart.framework.core.dao;

import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.transform.ResultTransformer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.stereotype.Repository;

import com.etheart.framework.core.FrameworkCoreException;
import com.etheart.framework.core.config.PagedListCallback;

@Repository(value = "etheart_QueryService")
public class QueryServiceHibernateImpl implements QueryService{

	private HibernateTemplate hibernateTemplate;

	@Autowired
	public QueryServiceHibernateImpl(SessionFactory sessionFactory){
		this.hibernateTemplate = new HibernateTemplate(sessionFactory);
	}

	@SuppressWarnings("unchecked")
	public <T> List<T> findAll(Class<T> clazz) {
		String hql = String.format("SELECT a FROM %s a", clazz.getSimpleName());
		return hibernateTemplate.find(hql);
	}

	@SuppressWarnings("unchecked")
	public <T> List<T> findAll(Class<T> clazz,String property,String order){
		String hql = String.format("SELECT a FROM %s a ORDER BY %s %s", clazz.getSimpleName(),property,order);
		return hibernateTemplate.find(hql);
	}

	@SuppressWarnings("unchecked")
	public <T> List<T> findByExample(T t) {
		return hibernateTemplate.findByExample(t);
	}

	public <T> long getRowCntByExample(T t) {
		DetachedCriteria criteria = DetachedCriteria.forClass(t.getClass());
		criteria.setProjection(Projections.rowCount()).add(Example.create(t));
		long rowCnt = (Long) hibernateTemplate.findByCriteria(criteria).get(0);
		return rowCnt;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public <T> Page<T> findPageByExample(final T e, int size) {
		//Search the first page at the begenning
		List<T> objects = hibernateTemplate.findByExample(e, 0, size);

		HibernateCallback<List<T>> callback = new PagedListCallback<List<T>>(){
			int firstResult;
			int maxResults;

			public List<T> doInHibernate(Session session) throws HibernateException,
	                        SQLException {
	        	return hibernateTemplate.findByExample(e, firstResult, maxResults);
	        }

			public void setFirstResult(int pFirstResult){
				this.firstResult = pFirstResult;
			}

			public void setMaxResults(int pMaxResults){
				this.maxResults = pMaxResults;
			}
		};
		Page<T> page = new Page(hibernateTemplate, callback);
		page.setObjects(objects);
		page.setPageSize(size);
		long rowCnt = this.getRowCntByExample(e);
		page.setRowCnt(rowCnt);
		return page;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public <T> Page<T> findAllPage(final Class<T> clazz, int size) {
		List<T> objects = findRange(clazz, 0, size);
		HibernateCallback callback = new PagedListCallback<List<T>>(){
			int firstResult;
			int maxResults;

			public List<T> doInHibernate(Session session) throws HibernateException,
	                        SQLException {
	        	return findRange(clazz, firstResult, maxResults);
	        }

			public void setFirstResult(int pFirstResult){
				this.firstResult = pFirstResult;
			}

			public void setMaxResults(int pMaxResults){
				this.maxResults = pMaxResults;
			}
		};
		Page<T> page = new Page<T>(hibernateTemplate, callback);
		page.setObjects(objects);
		page.setPageSize(size);
		long rowCnt = this.getRowCount(clazz);
		page.setRowCnt(rowCnt);
		return page;
	}

	public <T> long getRowCount(Class<T> clazz) {
		String hql = String.format("SELECT COUNT(*) FROM %s a ", clazz.getSimpleName());
        long rowCnt = ((Long) hibernateTemplate.find(hql).get(0)).intValue();
        return rowCnt;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public <T> List<T> findRange(Class<T> clazz, final int start, final int size) {
		if (start < 0 || size < 1){
			throw new IllegalArgumentException("起始行号不能小于 0 或最多记录数不能小于 1");
		}
		final String hql = String.format("SELECT a FROM %s a ", clazz.getSimpleName());
		return hibernateTemplate.executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
				Query query = session.createQuery(hql);
				query.setFirstResult(start);
				query.setMaxResults(size);
				return query.list();
			}
		});
	}


	@SuppressWarnings("unchecked")
	public <T> List<T> findListByDetachedCriteria(DetachedCriteria criteria) {
		return hibernateTemplate.findByCriteria(criteria);
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public <T> Page<T> findPageByDetachedCriteria(final DetachedCriteria criteria, final int size) {
		//Default search from the 0 - page size

		List<T> objects = hibernateTemplate.findByCriteria(criteria, 0, size);
		HibernateCallback callback = new PagedListCallback<List<T>>(){
			int firstResult;
			int maxResults;

			public List<T> doInHibernate(Session session) throws HibernateException,
	                        SQLException {
	        	return hibernateTemplate.findByCriteria(criteria, firstResult, maxResults);
	        }

			public void setFirstResult(int pFirstResult){
				this.firstResult = pFirstResult;
			}

			public void setMaxResults(int pMaxResults){
				this.maxResults = pMaxResults;
			}
		};

		Page<T> page = new Page<T>(hibernateTemplate, callback);
		//Page<T> page = new Page<T>(hibernateTemplate, criteria);
		page.setObjects(objects);
		page.setPageSize(size);


		Field originalCriteriaField = null;
		Field originalImplField = null;
		Field orderEntriesField = null;

		try {
			originalCriteriaField = criteria.getClass().getDeclaredField("criteria");
			originalImplField = criteria.getClass().getDeclaredField("impl");
			originalCriteriaField.setAccessible(true);
			originalImplField.setAccessible(true);

			CriteriaImpl criteriaImpl = (CriteriaImpl) originalImplField.get(criteria);

			Projection originalProjection = criteriaImpl.getProjection();
			Criteria originalProjectionCriteria = criteriaImpl.getProjectionCriteria();
			ResultTransformer orignialResultTransformer = criteriaImpl.getResultTransformer();


			DetachedCriteria rowCntCriteria = DetachedCriteria.forEntityName(criteriaImpl.getEntityOrClassName());
			//注入属性
			originalCriteriaField.set(rowCntCriteria, originalCriteriaField.get(criteria));
			originalImplField.set(rowCntCriteria,  criteriaImpl);
			rowCntCriteria.setProjection(Projections.rowCount());

			//去除排序条件orderEntries
			orderEntriesField = criteriaImpl.getClass().getDeclaredField("orderEntries");
			orderEntriesField.setAccessible(true);
			List<T> objectList = (List<T>) orderEntriesField.get(criteriaImpl);
			List<T>	copyObjectList = new ArrayList<T>();
			copyObjectList.addAll(objectList);
			objectList.removeAll(objectList);

			long rowCnt = (Long) hibernateTemplate.findByCriteria(rowCntCriteria).get(0);
			page.setRowCnt(rowCnt);

			//还原排序条件
			orderEntriesField.set(criteriaImpl, copyObjectList);

			criteriaImpl.setProjection(originalProjection);
			criteriaImpl.setResultTransformer(orignialResultTransformer);

			Field projectionCrieriaField = criteriaImpl.getClass().getDeclaredField("projectionCriteria");
			projectionCrieriaField.setAccessible(true);
			projectionCrieriaField.set(criteriaImpl, originalProjectionCriteria);


		} catch (SecurityException e) {
			throw new FrameworkCoreException("SecurityException: ", e);
		} catch (NoSuchFieldException e) {
			throw new FrameworkCoreException("NoSuchFieldException: ", e);
		}catch (IllegalArgumentException e) {
			throw new FrameworkCoreException("IllegalArgumentException: ", e);
		} catch (IllegalAccessException e) {
			throw new FrameworkCoreException("IllegalAccessException: ", e);
		}

		//page.setRowCnt(1000);
		return page;
	}

	public long getRowCount(DetachedCriteria criteria) {
		criteria.setProjection(Projections.rowCount());
		long rowCnt = (Long) hibernateTemplate.findByCriteria(criteria).get(0);
		return rowCnt;
	}

	@SuppressWarnings("unchecked")
	public <T> List<T> findListByDetachedCriteria(DetachedCriteria criteria,
			int first, int count) {
		return hibernateTemplate.findByCriteria(criteria, first,count);
	}

	@Override
	public <T> T findById(Class<T> clazz, Long id) {
		return hibernateTemplate.get(clazz, id);
	}



}
